/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. *//* Inline members for javascript type inference. */#ifndef vm_TypeInference_inl_h#define vm_TypeInference_inl_h#include"vm/TypeInference.h"#include"mozilla/BinarySearch.h"#include"mozilla/Casting.h"#include"mozilla/PodOperations.h"#include"builtin/SymbolObject.h"#include"jit/BaselineJIT.h"#include"vm/ArrayObject.h"#include"vm/BooleanObject.h"#include"vm/NumberObject.h"#include"vm/SharedArrayObject.h"#include"vm/StringObject.h"#include"vm/TypedArrayObject.h"#include"vm/UnboxedObject.h"#include"jscntxtinlines.h"#include"vm/ObjectGroup-inl.h"namespacejs{/////////////////////////////////////////////////////////////////////// CompilerOutput & RecompileInfo/////////////////////////////////////////////////////////////////////inlinejit::IonScript*CompilerOutput::ion()const{// Note: If type constraints are generated before compilation has finished// (i.e. after IonBuilder but before CodeGenerator::link) then a valid// CompilerOutput may not yet have an associated IonScript.MOZ_ASSERT(isValid());jit::IonScript*ion=script()->maybeIonScript();MOZ_ASSERT(ion!=ION_COMPILING_SCRIPT);returnion;}inlineCompilerOutput*RecompileInfo::compilerOutput(TypeZone&types)const{if(generation!=types.generation){if(!types.sweepCompilerOutputs||outputIndex>=types.sweepCompilerOutputs->length())returnnullptr;CompilerOutput*output=&(*types.sweepCompilerOutputs)[outputIndex];if(!output->isValid())returnnullptr;output=&(*types.compilerOutputs)[output->sweepIndex()];returnoutput->isValid()?output:nullptr;}if(!types.compilerOutputs||outputIndex>=types.compilerOutputs->length())returnnullptr;CompilerOutput*output=&(*types.compilerOutputs)[outputIndex];returnoutput->isValid()?output:nullptr;}inlineCompilerOutput*RecompileInfo::compilerOutput(JSContext*cx)const{returncompilerOutput(cx->zone()->types);}inlineboolRecompileInfo::shouldSweep(TypeZone&types){CompilerOutput*output=compilerOutput(types);if(!output||!output->isValid())returntrue;// If this info is for a compilation that occurred after sweeping started,// the index is already correct.MOZ_ASSERT_IF(generation==types.generation,outputIndex==output-types.compilerOutputs->begin());// Update this info for the output's index in the zone's compiler outputs.outputIndex=output-types.compilerOutputs->begin();generation=types.generation;returnfalse;}/////////////////////////////////////////////////////////////////////// Types//////////////////////////////////////////////////////////////////////* static */inlineTypeSet::ObjectKey*TypeSet::ObjectKey::get(JSObject*obj){MOZ_ASSERT(obj);if(obj->isSingleton())return(ObjectKey*)(uintptr_t(obj)|1);return(ObjectKey*)obj->group();}/* static */inlineTypeSet::ObjectKey*TypeSet::ObjectKey::get(ObjectGroup*group){MOZ_ASSERT(group);if(group->singleton())return(ObjectKey*)(uintptr_t(group->singleton())|1);return(ObjectKey*)group;}inlineObjectGroup*TypeSet::ObjectKey::groupNoBarrier(){MOZ_ASSERT(isGroup());return(ObjectGroup*)this;}inlineJSObject*TypeSet::ObjectKey::singletonNoBarrier(){MOZ_ASSERT(isSingleton());return(JSObject*)(uintptr_t(this)&~1);}inlineObjectGroup*TypeSet::ObjectKey::group(){ObjectGroup*res=groupNoBarrier();ObjectGroup::readBarrier(res);returnres;}inlineJSObject*TypeSet::ObjectKey::singleton(){JSObject*res=singletonNoBarrier();JSObject::readBarrier(res);returnres;}inlineJSCompartment*TypeSet::ObjectKey::maybeCompartment(){if(isSingleton())returnsingleton()->compartment();returngroup()->compartment();}/* static */inlineTypeSet::TypeTypeSet::ObjectType(JSObject*obj){if(obj->isSingleton())returnType(uintptr_t(obj)|1);returnType(uintptr_t(obj->group()));}/* static */inlineTypeSet::TypeTypeSet::ObjectType(ObjectGroup*group){if(group->singleton())returnType(uintptr_t(group->singleton())|1);returnType(uintptr_t(group));}/* static */inlineTypeSet::TypeTypeSet::ObjectType(ObjectKey*obj){returnType(uintptr_t(obj));}inlineTypeSet::TypeTypeSet::GetValueType(constValue&val){if(val.isDouble())returnTypeSet::DoubleType();if(val.isObject())returnTypeSet::ObjectType(&val.toObject());returnTypeSet::PrimitiveType(val.extractNonDoubleType());}inlineboolTypeSet::IsUntrackedValue(constValue&val){returnval.isMagic()&&(val.whyMagic()==JS_OPTIMIZED_OUT||val.whyMagic()==JS_UNINITIALIZED_LEXICAL);}inlineTypeSet::TypeTypeSet::GetMaybeUntrackedValueType(constValue&val){returnIsUntrackedValue(val)?UnknownType():GetValueType(val);}inlineTypeFlagsPrimitiveTypeFlag(JSValueTypetype){switch(type){caseJSVAL_TYPE_UNDEFINED:returnTYPE_FLAG_UNDEFINED;caseJSVAL_TYPE_NULL:returnTYPE_FLAG_NULL;caseJSVAL_TYPE_BOOLEAN:returnTYPE_FLAG_BOOLEAN;caseJSVAL_TYPE_INT32:returnTYPE_FLAG_INT32;caseJSVAL_TYPE_DOUBLE:returnTYPE_FLAG_DOUBLE;caseJSVAL_TYPE_STRING:returnTYPE_FLAG_STRING;caseJSVAL_TYPE_SYMBOL:returnTYPE_FLAG_SYMBOL;caseJSVAL_TYPE_MAGIC:returnTYPE_FLAG_LAZYARGS;default:MOZ_CRASH("Bad JSValueType");}}inlineJSValueTypeTypeFlagPrimitive(TypeFlagsflags){switch(flags){caseTYPE_FLAG_UNDEFINED:returnJSVAL_TYPE_UNDEFINED;caseTYPE_FLAG_NULL:returnJSVAL_TYPE_NULL;caseTYPE_FLAG_BOOLEAN:returnJSVAL_TYPE_BOOLEAN;caseTYPE_FLAG_INT32:returnJSVAL_TYPE_INT32;caseTYPE_FLAG_DOUBLE:returnJSVAL_TYPE_DOUBLE;caseTYPE_FLAG_STRING:returnJSVAL_TYPE_STRING;caseTYPE_FLAG_SYMBOL:returnJSVAL_TYPE_SYMBOL;caseTYPE_FLAG_LAZYARGS:returnJSVAL_TYPE_MAGIC;default:MOZ_CRASH("Bad TypeFlags");}}/* * Get the canonical representation of an id to use when doing inference. This * maintains the constraint that if two different jsids map to the same property * in JS (e.g. 3 and "3"), they have the same type representation. */inlinejsidIdToTypeId(jsidid){MOZ_ASSERT(!JSID_IS_EMPTY(id));// All properties which can be stored in an object's dense elements must// map to the aggregate property for index types.returnJSID_IS_INT(id)?JSID_VOID:id;}constchar*TypeIdStringImpl(jsidid);/* Convert an id for printing during debug. */staticinlineconstchar*TypeIdString(jsidid){#ifdef DEBUGreturnTypeIdStringImpl(id);#elsereturn"(missing)";#endif}/* * Structure for type inference entry point functions. All functions which can * change type information must use this, and functions which depend on * intermediate types (i.e. JITs) can use this to ensure that intermediate * information is not collected and does not change. * * Ensures that GC cannot occur. Does additional sanity checking that inference * is not reentrant and that recompilations occur properly. */structAutoEnterAnalysis{// For use when initializing an UnboxedLayout. The UniquePtr's destructor// must run when GC is not suppressed.UniquePtr<UnboxedLayout>unboxedLayoutToCleanUp;// Prevent GC activity in the middle of analysis.gc::AutoSuppressGCsuppressGC;// Allow clearing inference info on OOM during incremental sweeping.mozilla::Maybe<AutoClearTypeInferenceStateOnOOM>oom;// Pending recompilations to perform before execution of JIT code can resume.RecompileInfoVectorpendingRecompiles;// Prevent us from calling the objectMetadataCallback.js::AutoSuppressAllocationMetadataBuildersuppressMetadata;FreeOp*freeOp;Zone*zone;explicitAutoEnterAnalysis(JSContext*cx):suppressGC(cx),suppressMetadata(cx){init(cx->defaultFreeOp(),cx->zone());}AutoEnterAnalysis(FreeOp*fop,Zone*zone):suppressGC(TlsContext.get()),suppressMetadata(zone){init(fop,zone);}~AutoEnterAnalysis(){if(this!=zone->types.activeAnalysis)return;zone->types.activeAnalysis=nullptr;if(!pendingRecompiles.empty())zone->types.processPendingRecompiles(freeOp,pendingRecompiles);}private:voidinit(FreeOp*fop,Zone*zone){#ifdef JS_CRASH_DIAGNOSTICSMOZ_RELEASE_ASSERT(CurrentThreadCanAccessZone(zone));#endifthis->freeOp=fop;this->zone=zone;if(!zone->types.activeAnalysis){MOZ_RELEASE_ASSERT(!zone->types.sweepingTypes);zone->types.activeAnalysis=this;}}};/////////////////////////////////////////////////////////////////////// Interface functions/////////////////////////////////////////////////////////////////////voidMarkIteratorUnknownSlow(JSContext*cx);voidTypeMonitorCallSlow(JSContext*cx,JSObject*callee,constCallArgs&args,boolconstructing);/* * Monitor a javascript call, either on entry to the interpreter or made * from within the interpreter. */inlinevoidTypeMonitorCall(JSContext*cx,constjs::CallArgs&args,boolconstructing){if(args.callee().is<JSFunction>()){JSFunction*fun=&args.callee().as<JSFunction>();if(fun->isInterpreted()&&fun->nonLazyScript()->types())TypeMonitorCallSlow(cx,&args.callee(),args,constructing);}}MOZ_ALWAYS_INLINEboolTrackPropertyTypes(JSObject*obj,jsidid){if(obj->hasLazyGroup()||obj->group()->unknownProperties())returnfalse;if(obj->isSingleton()&&!obj->group()->maybeGetProperty(id))returnfalse;returntrue;}voidEnsureTrackPropertyTypes(JSContext*cx,JSObject*obj,jsidid);inlineboolCanHaveEmptyPropertyTypesForOwnProperty(JSObject*obj){// Per the comment on TypeSet::propertySet, property type sets for global// objects may be empty for 'own' properties if the global property still// has its initial undefined value.returnobj->is<GlobalObject>();}inlineboolPropertyHasBeenMarkedNonConstant(JSObject*obj,jsidid){// Non-constant properties are only relevant for singleton objects.if(!obj->isSingleton())returntrue;// EnsureTrackPropertyTypes must have been called on this object.if(obj->group()->unknownProperties())returntrue;HeapTypeSet*types=obj->group()->maybeGetProperty(IdToTypeId(id));returntypes->nonConstantProperty();}MOZ_ALWAYS_INLINEboolHasTrackedPropertyType(JSObject*obj,jsidid,TypeSet::Typetype){MOZ_ASSERT(id==IdToTypeId(id));MOZ_ASSERT(TrackPropertyTypes(obj,id));if(HeapTypeSet*types=obj->group()->maybeGetProperty(id)){if(!types->hasType(type))returnfalse;// Non-constant properties are only relevant for singleton objects.if(obj->isSingleton()&&!types->nonConstantProperty())returnfalse;returntrue;}returnfalse;}MOZ_ALWAYS_INLINEboolHasTypePropertyId(JSObject*obj,jsidid,TypeSet::Typetype){id=IdToTypeId(id);if(!TrackPropertyTypes(obj,id))returntrue;returnHasTrackedPropertyType(obj,id,type);}MOZ_ALWAYS_INLINEboolHasTypePropertyId(JSObject*obj,jsidid,constValue&value){returnHasTypePropertyId(obj,id,TypeSet::GetValueType(value));}voidAddTypePropertyId(JSContext*cx,ObjectGroup*group,JSObject*obj,jsidid,TypeSet::Typetype);voidAddTypePropertyId(JSContext*cx,ObjectGroup*group,JSObject*obj,jsidid,constValue&value);/* Add a possible type for a property of obj. */MOZ_ALWAYS_INLINEvoidAddTypePropertyId(JSContext*cx,JSObject*obj,jsidid,TypeSet::Typetype){id=IdToTypeId(id);if(TrackPropertyTypes(obj,id)&&!HasTrackedPropertyType(obj,id,type))AddTypePropertyId(cx,obj->group(),obj,id,type);}MOZ_ALWAYS_INLINEvoidAddTypePropertyId(JSContext*cx,JSObject*obj,jsidid,constValue&value){returnAddTypePropertyId(cx,obj,id,TypeSet::GetValueType(value));}inlinevoidMarkObjectGroupFlags(JSContext*cx,JSObject*obj,ObjectGroupFlagsflags){if(!obj->hasLazyGroup()&&!obj->group()->hasAllFlags(flags))obj->group()->setFlags(cx,flags);}inlinevoidMarkObjectGroupUnknownProperties(JSContext*cx,ObjectGroup*obj){if(!obj->unknownProperties())obj->markUnknown(cx);}inlinevoidMarkTypePropertyNonData(JSContext*cx,JSObject*obj,jsidid){id=IdToTypeId(id);if(TrackPropertyTypes(obj,id))obj->group()->markPropertyNonData(cx,obj,id);}inlinevoidMarkTypePropertyNonWritable(JSContext*cx,JSObject*obj,jsidid){id=IdToTypeId(id);if(TrackPropertyTypes(obj,id))obj->group()->markPropertyNonWritable(cx,obj,id);}/* Mark a state change on a particular object. */inlinevoidMarkObjectStateChange(JSContext*cx,JSObject*obj){if(!obj->hasLazyGroup()&&!obj->group()->unknownProperties())obj->group()->markStateChange(cx);}/* Interface helpers for JSScript*. */externvoidTypeMonitorResult(JSContext*cx,JSScript*script,jsbytecode*pc,TypeSet::Typetype);externvoidTypeMonitorResult(JSContext*cx,JSScript*script,jsbytecode*pc,StackTypeSet*types,TypeSet::Typetype);externvoidTypeMonitorResult(JSContext*cx,JSScript*script,jsbytecode*pc,constValue&rval);/////////////////////////////////////////////////////////////////////// Script interface functions//////////////////////////////////////////////////////////////////////* static */inlineunsignedTypeScript::NumTypeSets(JSScript*script){size_tnum=script->nTypeSets()+1/* this */;if(JSFunction*fun=script->functionNonDelazifying())num+=fun->nargs();returnnum;}/* static */inlineStackTypeSet*TypeScript::ThisTypes(JSScript*script){TypeScript*types=script->types();returntypes?types->typeArray()+script->nTypeSets():nullptr;}/* * Note: for non-escaping arguments, argTypes reflect only the initial type of * the variable (e.g. passed values for argTypes, or undefined for localTypes) * and not types from subsequent assignments. *//* static */inlineStackTypeSet*TypeScript::ArgTypes(JSScript*script,unsignedi){MOZ_ASSERT(i<script->functionNonDelazifying()->nargs());TypeScript*types=script->types();returntypes?types->typeArray()+script->nTypeSets()+1+i:nullptr;}template<typenameTYPESET>/* static */inlineTYPESET*TypeScript::BytecodeTypes(JSScript*script,jsbytecode*pc,uint32_t*bytecodeMap,uint32_t*hint,TYPESET*typeArray){MOZ_ASSERT(CodeSpec[*pc].format&JOF_TYPESET);uint32_toffset=script->pcToOffset(pc);// See if this pc is the next typeset opcode after the last one looked up.if((*hint+1)<script->nTypeSets()&&bytecodeMap[*hint+1]==offset){(*hint)++;returntypeArray+*hint;}// See if this pc is the same as the last one looked up.if(bytecodeMap[*hint]==offset)returntypeArray+*hint;// Fall back to a binary search. We'll either find the exact offset, or// there are more JOF_TYPESET opcodes than nTypeSets in the script (as can// happen if the script is very long) and we'll use the last location.size_tloc;#ifdef DEBUGboolfound=#endifmozilla::BinarySearch(bytecodeMap,0,script->nTypeSets()-1,offset,&loc);MOZ_ASSERT_IF(found,bytecodeMap[loc]==offset);*hint=mozilla::AssertedCast<uint32_t>(loc);returntypeArray+*hint;}/* static */inlineStackTypeSet*TypeScript::BytecodeTypes(JSScript*script,jsbytecode*pc){MOZ_ASSERT(CurrentThreadCanAccessZone(script->zone()));TypeScript*types=script->types();if(!types)returnnullptr;uint32_t*hint=script->baselineScript()->bytecodeTypeMap()+script->nTypeSets();returnBytecodeTypes(script,pc,script->baselineScript()->bytecodeTypeMap(),hint,types->typeArray());}/* static */inlinevoidTypeScript::Monitor(JSContext*cx,JSScript*script,jsbytecode*pc,constjs::Value&rval){TypeMonitorResult(cx,script,pc,rval);}/* static */inlinevoidTypeScript::Monitor(JSContext*cx,JSScript*script,jsbytecode*pc,TypeSet::Typetype){TypeMonitorResult(cx,script,pc,type);}/* static */inlinevoidTypeScript::Monitor(JSContext*cx,constjs::Value&rval){jsbytecode*pc;RootedScriptscript(cx,cx->currentScript(&pc));Monitor(cx,script,pc,rval);}/* static */inlinevoidTypeScript::Monitor(JSContext*cx,JSScript*script,jsbytecode*pc,StackTypeSet*types,constjs::Value&rval){TypeSet::Typetype=TypeSet::GetValueType(rval);if(!types->hasType(type))TypeMonitorResult(cx,script,pc,types,type);}/* static */inlinevoidTypeScript::MonitorAssign(JSContext*cx,HandleObjectobj,jsidid){if(!obj->isSingleton()){/* * Mark as unknown any object which has had dynamic assignments to * non-integer properties at SETELEM opcodes. This avoids making large * numbers of type properties for hashmap-style objects. We don't need * to do this for objects with singleton type, because type properties * are only constructed for them when analyzed scripts depend on those * specific properties. */uint32_ti;if(IdIsIndex(id,&i))return;// But if we don't have too many properties yet, don't do anything. The// idea here is that normal object initialization should not trigger// deoptimization in most cases, while actual usage as a hashmap should.ObjectGroup*group=obj->group();if(group->basePropertyCount()<128)return;MarkObjectGroupUnknownProperties(cx,group);}}/* static */inlinevoidTypeScript::SetThis(JSContext*cx,JSScript*script,TypeSet::Typetype){assertSameCompartment(cx,script,type);StackTypeSet*types=ThisTypes(script);if(!types)return;if(!types->hasType(type)){AutoEnterAnalysisenter(cx);InferSpew(ISpewOps,"externalType: setThis %p: %s",script,TypeSet::TypeString(type));types->addType(cx,type);}}/* static */inlinevoidTypeScript::SetThis(JSContext*cx,JSScript*script,constjs::Value&value){SetThis(cx,script,TypeSet::GetValueType(value));}/* static */inlinevoidTypeScript::SetArgument(JSContext*cx,JSScript*script,unsignedarg,TypeSet::Typetype){assertSameCompartment(cx,script,type);StackTypeSet*types=ArgTypes(script,arg);if(!types)return;if(!types->hasType(type)){AutoEnterAnalysisenter(cx);InferSpew(ISpewOps,"externalType: setArg %p %u: %s",script,arg,TypeSet::TypeString(type));types->addType(cx,type);}}/* static */inlinevoidTypeScript::SetArgument(JSContext*cx,JSScript*script,unsignedarg,constjs::Value&value){SetArgument(cx,script,arg,TypeSet::GetValueType(value));}/////////////////////////////////////////////////////////////////////// TypeHashSet/////////////////////////////////////////////////////////////////////// Hashing code shared by objects in TypeSets and properties in ObjectGroups.structTypeHashSet{// The sets of objects in a type set grow monotonically, are usually empty,// almost always small, and sometimes big. For empty or singleton sets, the// the pointer refers directly to the value. For sets fitting into// SET_ARRAY_SIZE, an array of this length is used to store the elements.// For larger sets, a hash table filled to 25%-50% of capacity is used,// with collisions resolved by linear probing.staticconstunsignedSET_ARRAY_SIZE=8;staticconstunsignedSET_CAPACITY_OVERFLOW=1u<<30;// Get the capacity of a set with the given element count.staticinlineunsignedCapacity(unsignedcount){MOZ_ASSERT(count>=2);MOZ_ASSERT(count<SET_CAPACITY_OVERFLOW);if(count<=SET_ARRAY_SIZE)returnSET_ARRAY_SIZE;return1u<<(mozilla::FloorLog2(count)+2);}// Compute the FNV hash for the low 32 bits of v.template<classT,classKEY>staticinlineuint32_tHashKey(Tv){uint32_tnv=KEY::keyBits(v);uint32_thash=84696351^(nv&0xff);hash=(hash*16777619)^((nv>>8)&0xff);hash=(hash*16777619)^((nv>>16)&0xff);return(hash*16777619)^((nv>>24)&0xff);}// Insert space for an element into the specified set and grow its capacity// if needed. returned value is an existing or new entry (nullptr if new).template<classT,classU,classKEY>staticU**InsertTry(LifoAlloc&alloc,U**&values,unsigned&count,Tkey){unsignedcapacity=Capacity(count);unsignedinsertpos=HashKey<T,KEY>(key)&(capacity-1);MOZ_RELEASE_ASSERT(uintptr_t(values[-1])==capacity);// Whether we are converting from a fixed array to hashtable.boolconverting=(count==SET_ARRAY_SIZE);if(!converting){while(values[insertpos]!=nullptr){if(KEY::getKey(values[insertpos])==key)return&values[insertpos];insertpos=(insertpos+1)&(capacity-1);}}if(count>=SET_CAPACITY_OVERFLOW)returnnullptr;count++;unsignednewCapacity=Capacity(count);if(newCapacity==capacity){MOZ_ASSERT(!converting);return&values[insertpos];}// Allocate an extra word right before the array storing the capacity,// for sanity checks.U**newValues=alloc.newArray<U*>(newCapacity+1);if(!newValues)returnnullptr;mozilla::PodZero(newValues,newCapacity+1);newValues[0]=(U*)uintptr_t(newCapacity);newValues++;for(unsignedi=0;i<capacity;i++){if(values[i]){unsignedpos=HashKey<T,KEY>(KEY::getKey(values[i]))&(newCapacity-1);while(newValues[pos]!=nullptr)pos=(pos+1)&(newCapacity-1);newValues[pos]=values[i];}}values=newValues;insertpos=HashKey<T,KEY>(key)&(newCapacity-1);while(values[insertpos]!=nullptr)insertpos=(insertpos+1)&(newCapacity-1);return&values[insertpos];}// Insert an element into the specified set if it is not already there,// returning an entry which is nullptr if the element was not there.template<classT,classU,classKEY>staticinlineU**Insert(LifoAlloc&alloc,U**&values,unsigned&count,Tkey){if(count==0){MOZ_ASSERT(values==nullptr);count++;return(U**)&values;}if(count==1){U*oldData=(U*)values;if(KEY::getKey(oldData)==key)return(U**)&values;// Allocate an extra word right before the array storing the// capacity, for sanity checks.values=alloc.newArray<U*>(SET_ARRAY_SIZE+1);if(!values){values=(U**)oldData;returnnullptr;}mozilla::PodZero(values,SET_ARRAY_SIZE+1);values[0]=(U*)uintptr_t(SET_ARRAY_SIZE);values++;count++;values[0]=oldData;return&values[1];}if(count<=SET_ARRAY_SIZE){MOZ_RELEASE_ASSERT(uintptr_t(values[-1])==SET_ARRAY_SIZE);for(unsignedi=0;i<count;i++){if(KEY::getKey(values[i])==key)return&values[i];}if(count<SET_ARRAY_SIZE){count++;return&values[count-1];}}returnInsertTry<T,U,KEY>(alloc,values,count,key);}// Lookup an entry in a hash set, return nullptr if it does not exist.template<classT,classU,classKEY>staticMOZ_ALWAYS_INLINEU*Lookup(U**values,unsignedcount,Tkey){if(count==0)returnnullptr;if(count==1)return(KEY::getKey((U*)values)==key)?(U*)values:nullptr;if(count<=SET_ARRAY_SIZE){MOZ_RELEASE_ASSERT(uintptr_t(values[-1])==SET_ARRAY_SIZE);for(unsignedi=0;i<count;i++){if(KEY::getKey(values[i])==key)returnvalues[i];}returnnullptr;}unsignedcapacity=Capacity(count);unsignedpos=HashKey<T,KEY>(key)&(capacity-1);MOZ_RELEASE_ASSERT(uintptr_t(values[-1])==capacity);while(values[pos]!=nullptr){if(KEY::getKey(values[pos])==key)returnvalues[pos];pos=(pos+1)&(capacity-1);}returnnullptr;}};/////////////////////////////////////////////////////////////////////// TypeSet/////////////////////////////////////////////////////////////////////inlineTypeSet::ObjectKey*TypeSet::Type::objectKey()const{MOZ_ASSERT(isObject());return(ObjectKey*)data;}inlineJSObject*TypeSet::Type::singleton()const{returnobjectKey()->singleton();}inlineObjectGroup*TypeSet::Type::group()const{returnobjectKey()->group();}inlineJSObject*TypeSet::Type::singletonNoBarrier()const{returnobjectKey()->singletonNoBarrier();}inlineObjectGroup*TypeSet::Type::groupNoBarrier()const{returnobjectKey()->groupNoBarrier();}inlinevoidTypeSet::Type::trace(JSTracer*trc){if(isSingletonUnchecked()){JSObject*obj=singletonNoBarrier();TraceManuallyBarrieredEdge(trc,&obj,"TypeSet::Object");*this=TypeSet::ObjectType(obj);}elseif(isGroupUnchecked()){ObjectGroup*group=groupNoBarrier();TraceManuallyBarrieredEdge(trc,&group,"TypeSet::Group");*this=TypeSet::ObjectType(group);}}inlineJSCompartment*TypeSet::Type::maybeCompartment(){if(isSingletonUnchecked())returnsingletonNoBarrier()->compartment();if(isGroupUnchecked())returngroupNoBarrier()->compartment();returnnullptr;}MOZ_ALWAYS_INLINEboolTypeSet::hasType(Typetype)const{if(unknown())returntrue;if(type.isUnknown()){returnfalse;}elseif(type.isPrimitive()){return!!(flags&PrimitiveTypeFlag(type.primitive()));}elseif(type.isAnyObject()){return!!(flags&TYPE_FLAG_ANYOBJECT);}else{return!!(flags&TYPE_FLAG_ANYOBJECT)||TypeHashSet::Lookup<ObjectKey*,ObjectKey,ObjectKey>(objectSet,baseObjectCount(),type.objectKey())!=nullptr;}}inlinevoidTypeSet::setBaseObjectCount(uint32_tcount){MOZ_ASSERT(count<=TYPE_FLAG_DOMOBJECT_COUNT_LIMIT);flags=(flags&~TYPE_FLAG_OBJECT_COUNT_MASK)|(count<<TYPE_FLAG_OBJECT_COUNT_SHIFT);}inlinevoidHeapTypeSet::newPropertyState(JSContext*cx){checkMagic();/* Propagate the change to all constraints. */if(!cx->helperThread()){TypeConstraint*constraint=constraintList();while(constraint){constraint->newPropertyState(cx,this);constraint=constraint->next();}}else{MOZ_ASSERT(!constraintList());}}inlinevoidHeapTypeSet::setNonDataProperty(JSContext*cx){checkMagic();if(flags&TYPE_FLAG_NON_DATA_PROPERTY)return;flags|=TYPE_FLAG_NON_DATA_PROPERTY;newPropertyState(cx);}inlinevoidHeapTypeSet::setNonWritableProperty(JSContext*cx){checkMagic();if(flags&TYPE_FLAG_NON_WRITABLE_PROPERTY)return;flags|=TYPE_FLAG_NON_WRITABLE_PROPERTY;newPropertyState(cx);}inlinevoidHeapTypeSet::setNonConstantProperty(JSContext*cx){checkMagic();if(flags&TYPE_FLAG_NON_CONSTANT_PROPERTY)return;flags|=TYPE_FLAG_NON_CONSTANT_PROPERTY;newPropertyState(cx);}inlineunsignedTypeSet::getObjectCount()const{MOZ_ASSERT(!unknownObject());uint32_tcount=baseObjectCount();if(count>TypeHashSet::SET_ARRAY_SIZE)returnTypeHashSet::Capacity(count);returncount;}inlineTypeSet::ObjectKey*TypeSet::getObject(unsignedi)const{MOZ_ASSERT(i<getObjectCount());if(baseObjectCount()==1){MOZ_ASSERT(i==0);return(ObjectKey*)objectSet;}returnobjectSet[i];}inlineJSObject*TypeSet::getSingleton(unsignedi)const{ObjectKey*key=getObject(i);return(key&&key->isSingleton())?key->singleton():nullptr;}inlineObjectGroup*TypeSet::getGroup(unsignedi)const{ObjectKey*key=getObject(i);return(key&&key->isGroup())?key->group():nullptr;}inlineJSObject*TypeSet::getSingletonNoBarrier(unsignedi)const{ObjectKey*key=getObject(i);return(key&&key->isSingleton())?key->singletonNoBarrier():nullptr;}inlineObjectGroup*TypeSet::getGroupNoBarrier(unsignedi)const{ObjectKey*key=getObject(i);return(key&&key->isGroup())?key->groupNoBarrier():nullptr;}inlineconstClass*TypeSet::getObjectClass(unsignedi)const{if(JSObject*object=getSingleton(i))returnobject->getClass();if(ObjectGroup*group=getGroup(i))returngroup->clasp();returnnullptr;}/////////////////////////////////////////////////////////////////////// ObjectGroup/////////////////////////////////////////////////////////////////////inlineuint32_tObjectGroup::basePropertyCount(){return(flags()&OBJECT_FLAG_PROPERTY_COUNT_MASK)>>OBJECT_FLAG_PROPERTY_COUNT_SHIFT;}inlinevoidObjectGroup::setBasePropertyCount(uint32_tcount){// Note: Callers must ensure they are performing threadsafe operations.MOZ_ASSERT(count<=OBJECT_FLAG_PROPERTY_COUNT_LIMIT);flags_=(flags()&~OBJECT_FLAG_PROPERTY_COUNT_MASK)|(count<<OBJECT_FLAG_PROPERTY_COUNT_SHIFT);}inlineHeapTypeSet*ObjectGroup::getProperty(JSContext*cx,JSObject*obj,jsidid){MOZ_ASSERT(JSID_IS_VOID(id)||JSID_IS_EMPTY(id)||JSID_IS_STRING(id)||JSID_IS_SYMBOL(id));MOZ_ASSERT_IF(!JSID_IS_EMPTY(id),id==IdToTypeId(id));MOZ_ASSERT(!unknownProperties());MOZ_ASSERT_IF(obj,obj->group()==this);MOZ_ASSERT_IF(singleton(),obj);if(HeapTypeSet*types=maybeGetProperty(id))returntypes;Property*base=cx->typeLifoAlloc().new_<Property>(id);if(!base){markUnknown(cx);returnnullptr;}uint32_tpropertyCount=basePropertyCount();Property**pprop=TypeHashSet::Insert<jsid,Property,Property>(cx->typeLifoAlloc(),propertySet,propertyCount,id);if(!pprop){markUnknown(cx);returnnullptr;}MOZ_ASSERT(!*pprop);setBasePropertyCount(propertyCount);*pprop=base;updateNewPropertyTypes(cx,obj,id,&base->types);if(propertyCount==OBJECT_FLAG_PROPERTY_COUNT_LIMIT){// We hit the maximum number of properties the object can have, mark// the object unknown so that new properties will not be added in the// future.markUnknown(cx);}base->types.checkMagic();return&base->types;}MOZ_ALWAYS_INLINEHeapTypeSet*ObjectGroup::maybeGetProperty(jsidid){MOZ_ASSERT(JSID_IS_VOID(id)||JSID_IS_EMPTY(id)||JSID_IS_STRING(id)||JSID_IS_SYMBOL(id));MOZ_ASSERT_IF(!JSID_IS_EMPTY(id),id==IdToTypeId(id));MOZ_ASSERT(!unknownProperties());Property*prop=TypeHashSet::Lookup<jsid,Property,Property>(propertySet,basePropertyCount(),id);if(!prop)returnnullptr;prop->types.checkMagic();return&prop->types;}inlineunsignedObjectGroup::getPropertyCount(){uint32_tcount=basePropertyCount();if(count>TypeHashSet::SET_ARRAY_SIZE)returnTypeHashSet::Capacity(count);returncount;}inlineObjectGroup::Property*ObjectGroup::getProperty(unsignedi){MOZ_ASSERT(i<getPropertyCount());Property*result;if(basePropertyCount()==1){MOZ_ASSERT(i==0);result=(Property*)propertySet;}else{result=propertySet[i];}if(result)result->types.checkMagic();returnresult;}}// namespace jsinlinejs::TypeScript*JSScript::types(){maybeSweepTypes(nullptr);returntypes_;}inlineboolJSScript::ensureHasTypes(JSContext*cx){returntypes()||makeTypes(cx);}#endif /* vm_TypeInference_inl_h */